package towerDefense.maps;

import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.geom.Point2D;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.ResourceBundle;
import java.util.Scanner;
import java.lang.Double;
import javax.imageio.ImageIO;
import towerDefense.TowerDefense;
import com.golden.gamedev.object.background.abstraction.AbstractTileBackground;


@SuppressWarnings("serial")
public class TDTiledMap extends AbstractTileBackground
    implements IMapPackageReader
{

    private static final String DIMENSIONS_KEY = "dimensions";
    private static final String IMAGEDIRECTORY_KEY = "imagedirectory";
    private static final String PATHID_KEY = "path";
    private String[][] myTileMap;
    private boolean[][] myOccupied;
    private Dimension myTileSize;
    private Dimension myMapSize;
    private HashMap<String, Image> myImages;
    String pathID;


    public TDTiledMap (Dimension mapDim, File mapdir)
    {
        super(mapDim.width, mapDim.height, 0, 0);
        myMapSize = mapDim;
        openPackage(mapdir);
    }


    @Override
    public void renderTile (Graphics2D g, int tileX, int tileY, int x, int y)
    {
        g.drawImage(myImages.get(myTileMap[tileY][tileX]), x, y, null);
    }


    @Override
    public void render (Graphics2D g,
                        int arg1,
                        int arg2,
                        int arg3,
                        int arg4,
                        int arg5,
                        int arg6)
    {
        for (int y = 0; y < myTileMap.length; y++)
        {
            for (int x = 0; x < myTileMap[y].length; x++)
            {
                renderTile(g, x, y, x * myTileSize.width, y * myTileSize.height);

            }
        }
    }


    @Override
    public void openPackage (File pack)
    {
        System.out.println(pack);
        File[] sub = pack.listFiles();
        setProperties(sub[0]);
        readMap(sub[1]);

    }


    @Override
    public void setProperties (File prop)
    {
        myImages = new HashMap<String, Image>();
        ResourceBundle rb =
            ResourceBundle.getBundle(prop.getPath().substring(4).split("\\.")[0].replaceAll("\\\\",
                                                                                            "\\."));
        String imagedir = (String) rb.getObject(IMAGEDIRECTORY_KEY);
        String[] mapSize = ((String) rb.getObject(DIMENSIONS_KEY)).split("\\,");
        pathID = (String) rb.getObject(PATHID_KEY);
        myTileMap =
            new String[Integer.parseInt(mapSize[1])][Integer.parseInt(mapSize[0])];
        myOccupied =
            new boolean[Integer.parseInt(mapSize[1])][Integer.parseInt(mapSize[0])];
        myTileSize =
            new Dimension((int) Math.ceil(myMapSize.width /
                                          Double.parseDouble(mapSize[0])),
                          (int) Math.ceil(myMapSize.height /
                                          Double.parseDouble(mapSize[1])));
        for (String key : rb.keySet())
        {
            if (key.equals(IMAGEDIRECTORY_KEY) || key.equals(DIMENSIONS_KEY) ||
                key.equals(PATHID_KEY))
            {
                continue;
            }
            try
            {
                myImages.put(key,
                             ImageIO.read(new File(imagedir +
                                                   ((String) rb.getObject(key))))
                                    .getScaledInstance(myTileSize.width,
                                                       myTileSize.height,
                                                       0));
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
        }

    }


    @Override
    public void readMap (File blueprint)
    {
        Scanner scan;
        try
        {
            scan = new Scanner(blueprint);
            ArrayList<String> rows = new ArrayList<String>();
            while (scan.hasNextLine())
            {
                rows.add(scan.nextLine());
            }
            for (int i = 0; i < myTileMap.length; i++)
            {
                for (int j = 0; j < myTileMap[0].length; j++)
                {
                    String tileID = Character.toString(rows.get(i).charAt(j));
                    myTileMap[i][j] = tileID;
                    if (tileID.equals(pathID)) myOccupied[i][j] = true;
                    else myOccupied[i][j] = false;
                }
            }
        }
        catch (FileNotFoundException e)
        {
            e.printStackTrace();
        }

    }


    public Dimension getTileDim ()
    {
        return myTileSize;
    }


    public boolean isOccupied (int x, int y)
    {
        return myOccupied[y][x];
    }


    public int convertToTileX (double x)
    {

        return (int) (x / getTileWidth());
    }


    public int convertToTileY (double y)
    {
        return (int) (y / getTileHeight());
    }


    @Override
    public int getTileWidth ()
    {
        return myTileSize.width;

    }


    @Override
    public int getTileHeight ()
    {
        return myTileSize.height;

    }


    public void setOccupied (int i, int j, boolean b)
    {
        myOccupied[j][i] = b;

    }


    /**
     * Dynamically update pathing to allow for mazing.
     */
//    public LinkedList<Point2D> calculatePathToLeft (double x, double y, int w, int l)
//    {
//        LinkedList<Point2D> path = new LinkedList<Point2D>();
////        double currX = x,currY = y,minX=x,minY=y,
////        minD = Math.sqrt(Math.pow(TowerDefense.DEFAULT_DIM.height, 2)+
////                         Math.pow(myTileSize.getWidth(), 2));
////        for (int n = convertToTileX(x); n >= 0 ; n--){
////            
////            path.add(new Point2D.Double(currX-w/2,currY-l/2));
////            for(int m = 0; m < myOccupied.length; m++){
////                if (!myOccupied[m][n]){
////                    double testD = Point2D.distance(currX, 
////                                                   currY, 
////                                                   n*myTileSize.width+w/2,
////                                                   m*myTileSize.height+l/2);
////                    if(testD < minD){
////                        minD = testD;
////                        minX = currX;
////                        minY = currY;
////                    }
////                }
////            }
////            currX = minX;
////            currY = minY;
////            System.out.println(currX + ","+ currY);
////        }
//        
//        Point2D p = new Point2D.Double(x,y);
//        int dx = 0, dy = 0;
//        int tX = convertToTileX(p.getX());
//        int tY = convertToTileX(p.getY());
//        while(tX >0){
//            if (!colXContainsTower(tX-1)){
//                dx-=1;
//                continue;
//            }
//            
//            if (!checkOccupied(convertToTileY(p.getY()), convertToTileX(p.getX())+1)){
//                p.setLocation(p.getX(), p.getY()+dy);
//                path.add(p);
//                dx-=myTileSize.width;
//            }
//            else if (!checkOccupied(convertToTileY(p.getY())-1,convertToTileX(p.getX()))){
//                dy-=myTileSize.height;
//                continue;
//            }
//            else if (!checkOccupied(convertToTileY(p.getY())+1,convertToTileX(p.getX()))){
//                p.setLocation(p.getX(), p.getY()+dy);
//                path.add(p);
//                dx+=myTileSize.width;
//            }
//            else if (!checkOccupied(convertToTileY(p.getY()),convertToTileX(p.getX())+1)){
//                dy+=myTileSize.height;
//                continue;
//            }
//            p.setLocation(p.getX()+dx, p.getY()+dy);
//            path.add(p);
//            dx=0;
//            dy=0;
//        }
//        p.setLocation(p.getX()+dx, p.getY()+dy);
//        System.out.println(p);
//
//        path.add(p);
//        return path;
//    }

    /**
     * @param p
     * @return
     */
    private boolean checkOccupied (int i, int j)
    {
        if (i >= myOccupied[0].length || j >= myOccupied.length || i < 0 ||
            j < 0) return true;
        return !myOccupied[i][j];
    }


    private boolean colXContainsTower (int x)
    {
        for (int y = 0; y < myOccupied.length; y++)
        {
            if (myOccupied[y][x]) return true;
        }
        return false;
    }


    @Override
    public int getTotalHorizontalTiles ()
    {
        return myTileMap.length;
    }


    @Override
    public int getTotalVerticalTiles ()
    {
        return myTileMap[0].length;
    }


    public LinkedList<Point2D> createMapPath ()
    {
        LinkedList<Point2D> path = new LinkedList<Point2D>();
        for (int j = 0; j < myTileMap.length; j++)
        {

            if (myTileMap[j][0].equals(pathID))
            {
                path.add(new Point2D.Double(myTileSize.width / 2,
                                            (j + 0.5) * myTileSize.height));
                System.out.println("yay");
            }
        }

        Point2D p = path.getFirst();

        while (p.getX() <= TowerDefense.DEFAULT_DIM.width)
        {
            p = getNextPath(p, path);
            if (p == null) break;
        }

        for (int j = 0; j < myTileMap.length; j++)
        {

            if (myTileMap[j][myTileMap[j].length - 1].equals(pathID))
            {
                path.add(new Point2D.Double(myTileSize.width *
                                                    (myTileMap[j].length + 1),
                                            (j + 0.5) * myTileSize.height));
                System.out.println("yay");
            }
        }
        return path;

    }


    private Point2D getNextPath (Point2D p, LinkedList<Point2D> path)
    {
        int[] dx = new int[] { 1, 0, -1, 0 };
        int[] dy = new int[] { 0, 1, 0, -1 };

        for (int i = 0; i < dx.length; i++)
        {
            Point2D test =
                new Point2D.Double(p.getX() + dx[i] * myTileSize.width,
                                   p.getY() + dy[i] * myTileSize.height);
            if (!path.contains(test) &&
                !isOutOfBounds(test) &&
                myTileMap[convertToTileY(test.getY())][convertToTileX(test.getX())].equals(pathID))
            {
                path.add(test);
                return test;
            }
        }
        return null;
    }


    public boolean isOutOfBounds (Point2D p)
    {
        double x = p.getX();
        double y = p.getY();
        return (x < 0 || y < 0 || x >= TowerDefense.DEFAULT_DIM.width || y >= TowerDefense.DEFAULT_DIM.height);
    }

}
